Skip to content

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claude claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

    if (!policy.enabled) {
      this.logger?.warn?.(
        '[share-link] redemption refused — publicSharing.enabled is not true on the object '
          + '(the block is switched off; its links resolve again once it is re-enabled)',
        {
          link: row.id,
          object: row.object_name,
          record: row.record_id,
          reason: 'SHARING_NOT_ENABLED',
        },
      );
      return null;
    }

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gate exit its own verdict line
node scripts/check-adr-0087-registration.mjs 0 ✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs 0 ✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs 0 ✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs 0 ✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs 0 green
node scripts/pm/release-rehearsal-clone.mjs --self-test 0 green
pnpm check:changeset-gate-self-tests 0 green
pnpm check:objectui-changeset 0 green
pnpm check:pm-half-states 0 green
node scripts/check-changeset-fixed.mjs (added) 0 ✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added) 0 check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)

The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.

Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.

Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-sales os-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@os-sales
os-sales enabled auto-merge September 3, 2026 00:10
@github-actions github-actions Bot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit 8d1f22f Sep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate tooling

Projects

None yet

2 participants